package com.sky.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sky.constant.RedisConstant;
import com.sky.domain.PmsCategory;
import com.sky.domain.PmsCategoryBrandRelation;
import com.sky.mapper.PmsCategoryBrandRelationMapper;
import com.sky.service.PmsCategoryService;
import com.sky.mapper.PmsCategoryMapper;
import com.sky.utils.StringUtils;
import com.sky.vo.web.Catalog3VO;
import com.sky.vo.web.CateLog2VO;
import jakarta.annotation.Resource;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author Administrator
 * @description 针对表【pms_category(商品三级分类)】的数据库操作Service实现
 * @createDate 2024-06-30 16:46:42
 */
@Service
public class PmsCategoryServiceImpl extends ServiceImpl<PmsCategoryMapper, PmsCategory> implements PmsCategoryService {

    @Resource
    private PmsCategoryBrandRelationMapper pmsCategoryBrandRelationMapper;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 根据数据库查询结果构建分类树列表
     * <p>
     * 此方法从基础Mapper获取所有类别，然后过滤出父类别ID为0的顶级类别。
     * 对每个顶级类别，通过递归调用获取子类别，并将子类别集合设置到父类别对象中。
     * 最后，根据排序字段对所有顶级类别进行排序，并收集到一个新的列表中返回。
     *
     * @return 返回一个排序后的PmsCategory列表，其中包含它们的子类别集合
     */
    @Override
    public List<PmsCategory> listAsTree() {
        // 获取所有类别
        List<PmsCategory> pmsCategoryList = baseMapper.selectList(null);

        // 过滤出父类别ID为0的顶级类别，并获取它们的子类别
        List<PmsCategory> categoryList = pmsCategoryList.stream()
                .filter(pmsCategory -> pmsCategory.getParentCid() == 0)
                .map(pmsCategory -> {
                    // 递归获取子类别并设置到当前类别中
                    List<PmsCategory> children = getChildrenTree(pmsCategory, pmsCategoryList);
                    pmsCategory.setChildren(children);
                    return pmsCategory;
                })
                // 按照排序字段排序
                .sorted(Comparator.comparing(PmsCategory::getSort))
                // 收集到一个新的列表中
                .collect(Collectors.toList());

        // 返回排序后的顶级类别列表，包含它们的子类别
        return categoryList;
    }

    /**
     * 获取指定分类及其子分类的树状结构列表。
     * 通过递归方式，将属于同一父分类的所有子分类组织成树状结构。
     *
     * @param pmsCategory 当前处理的分类对象，作为查找子分类的依据。
     * @param pmsCategoryList 所有分类的列表，用于查找子分类。
     * @return 返回包含当前分类及其所有子分类的树状结构列表。
     */
    private List<PmsCategory> getChildrenTree(PmsCategory pmsCategory, List<PmsCategory> pmsCategoryList) {
        // 使用流式编程，过滤出父分类为当前分类的所有子分类
        List<PmsCategory> categoryList = pmsCategoryList.stream()
                .filter(data -> data.getParentCid().equals(pmsCategory.getCatId()))
                // 对每个子分类递归调用本方法，获取其子分类，并设置到当前分类中
                .map(data -> {
                    List<PmsCategory> children = getChildrenTree(data, pmsCategoryList);
                    data.setChildren(children);
                    return data;
                })
                // 按照分类的排序值进行排序
                .sorted(Comparator.comparing(PmsCategory::getSort))
                // 收集处理后的分类列表
                .collect(Collectors.toList());
        return categoryList;
    }


    /**
     * 获取分类路径。
     * <p>
     * 该方法通过递归的方式获取指定分类ID的路径，即从根分类到指定分类。
     *
     * @param catelogId 指定分类ID
     * @param path      用于存储分类路径的列表
     * @return List<Long> 返回一个包含分类ID的列表，列表中的元素按照从根分类到指定分类的顺序排列。
     */
    @Override
    public List<Long> getPath(Long catelogId,List<Long> path) {
        PmsCategory pmsCategory = baseMapper.selectById(catelogId);
        if (pmsCategory.getParentCid() != 0){
            PmsCategory parentCateGory = baseMapper.selectOne(new LambdaQueryWrapper<PmsCategory>().eq(PmsCategory::getCatId, pmsCategory.getParentCid()));
            getPath(parentCateGory.getCatId(),path);
        }
        path.add(pmsCategory.getCatId());
        return path;
    }

    /**
     * 更新分类信息。
     * <p>
     * 该方法用于更新分类信息，包括分类名称、分类图标、分类描述等。
     *
     * @param pmsCategory 包含更新信息的分类对象
     * @return boolean 返回一个布尔值，指示更新是否成功。
     */
    @Override
    public boolean updateCategory(PmsCategory pmsCategory) {
        baseMapper.updateById(pmsCategory);

        String categoryName = pmsCategory.getName();
        if (StringUtils.isNotEmpty(categoryName)){
            pmsCategoryBrandRelationMapper.update(null,new LambdaUpdateWrapper<PmsCategoryBrandRelation>()
                    .eq(PmsCategoryBrandRelation::getCatalogId, pmsCategory.getCatId())
                    .set(PmsCategoryBrandRelation::getBrandName,categoryName));
        }

        return true;
    }

    /**
     * 获取顶级分类列表。
     * <p>
     * 该方法用于获取所有顶级分类列表，即没有父分类的分类。
     *
     * @return List<PmsCategory> 返回一个包含顶级分类的列表。
     */
    @Override
    @Cacheable(value = RedisConstant.PRODUCT_CATEGORY,key = "#root.method.name")
    public List<PmsCategory> getCategoryTopLevel() {
        return this.list(new LambdaQueryWrapper<PmsCategory>().eq(PmsCategory::getParentCid, 0));
    }

    /**
     * 获取分类JSON数据。
     * <p>
     * 该方法用于获取分类JSON数据，包括一级分类、二级分类和三级分类。
     *
     * @return Map<String, List < CateLog2VO>> 返回一个包含一级分类、二级分类和三级分类的Map。
     */
    @Override
    public Map<String, List<CateLog2VO>> getCatalogJson() {
        // 查询redis缓存是否有数据
        String categoryStr = stringRedisTemplate.opsForValue().get(RedisConstant.PRODUCT_CATEGORY_JSON_KEY);
        if (StringUtils.isEmpty(categoryStr)){
            // redis缓存中无数据，加分布式锁
            RLock lock = redissonClient.getLock(RedisConstant.PRODUCT_CATEGORY_LOCK);
            lock.lock();
            try {
                // 双检锁
                categoryStr = stringRedisTemplate.opsForValue().get(RedisConstant.PRODUCT_CATEGORY_JSON_KEY);
                if (StringUtils.isEmpty(categoryStr)){
                    // redis缓存中仍然无数据，从mysql数据库中查询数据
                    Map<String, List<CateLog2VO>> categoryFromDB = getCategoryFromDB();
                    // 将数据存入redis缓存中
                    stringRedisTemplate.opsForValue().set(RedisConstant.PRODUCT_CATEGORY_JSON_KEY, JSON.toJSONString(categoryFromDB));
                    return categoryFromDB;
                } else {
                    // redis缓存中存在数据，直接返回
                    return JSON.parseObject(categoryStr, new TypeReference<Map<String, List<CateLog2VO>>>() {});
                }
            } finally {
                // 解锁
                lock.unlock();
            }
        }
        return JSON.parseObject(categoryStr, new TypeReference<Map<String, List<CateLog2VO>>>() {});
    }


    private Map<String, List<CateLog2VO>> getCategoryFromDB() {

        Map<String, List<CateLog2VO>> result = new HashMap<>();
        // 查询所有的产品分类
        List<PmsCategory> categories = this.list();
        // 获取所有的一级节点
        List<PmsCategory> categoryTopLevels = categories.stream().filter(category -> category.getCatLevel() == 1).collect(Collectors.toList());
        // 所有的二级分类进行分组
        Map<Long, List<PmsCategory>> secondLevelCategory = categories.stream().filter(category -> category.getCatLevel() == 2).collect(Collectors.groupingBy(PmsCategory::getParentCid));
        // 所有的三级分类进行分组
        Map<Long, List<PmsCategory>> thirdLevelCategory = categories.stream().filter(category -> category.getCatLevel() == 3).collect(Collectors.groupingBy(PmsCategory::getParentCid));
        // 遍历一级分类，封装二级分类
        for (PmsCategory categoryTopLevel : categoryTopLevels) {
            List<CateLog2VO> log2VOList = new ArrayList<>();
            // 查询二级分类
            List<PmsCategory> list = secondLevelCategory.get(categoryTopLevel.getCatId());
            for (PmsCategory pmsCategory : list) {
                // 查询三级分类
                List<PmsCategory> pmsCategoryList = thirdLevelCategory.get(pmsCategory.getCatId());
                List<Catalog3VO> voList = null;
                if (StringUtils.isNotEmpty(pmsCategoryList)){
                    voList = pmsCategoryList.stream().map(data -> {
                        Catalog3VO catalog3VO = new Catalog3VO(pmsCategory.getCatId().toString(), data.getCatId().toString(), data.getName());
                        return catalog3VO;
                    }).collect(Collectors.toList());
                }
                CateLog2VO cateLog2VO = new CateLog2VO(categoryTopLevel.getCatId().toString(), voList, pmsCategory.getCatId().toString(), pmsCategory.getName());
                log2VOList.add(cateLog2VO);
            }
            result.put(categoryTopLevel.getCatId().toString(), log2VOList);
        }
        return result;
    }
}




